

#include <QRunnable>
#include <QObject>
#include <QString>
#include <QDebug>
#include <QThread>
#include <QByteArray>
#include <QRandomGenerator>
#include <cstring>
#include "comm_utils.h"
#include "task_comm.h"
#include "ev_cmd_type.h"

#define DUMP_SEND_PKT LogUtils::makeLogBin("-- 发送指令为:", send_pkt.get())
#define DUMP_RECV_PKT LogUtils::makeLogBin("-- 接收指令为:", recv_pkt.get())

#define CHECK_RECV_PKT(length) (recv_pkt->cmd == (send_pkt->cmd + 1) && \
                                recv_pkt->len == length && \
                                recv_pkt->seq == send_pkt->seq && \
                                recv_pkt->user == send_pkt->user )

#define PREPARE_PKT(command, req_type) \
    auto send_pkt = std::make_shared<ev_packet_t>(); \
    send_pkt->seq = seq ++; \
    send_pkt->user = 0; \
    send_pkt->cmd = command; \
    send_pkt->len = sizeof(req_type); \
    req_type *req = (req_type *)send_pkt->data;

#define PREPARE_PKT1(command) \
    auto send_pkt = std::make_shared<ev_packet_t>(); \
    send_pkt->seq = seq ++; \
    send_pkt->user = 0; \
    send_pkt->cmd = command; \
    send_pkt->len = 0;

#define SEND_RECV_PKT(ms, rsp_type) \
    if (!EvSerial::safeSendPacket(m_serial, send_pkt)) { \
        emit messageLogged(DUMP_SEND_PKT); \
        return -1; \
    } \
    auto recv_pkt = EvSerial::safeRecvPacket(m_serial, ms); \
    if (!recv_pkt) { \
        emit messageLogged(DUMP_SEND_PKT); \
        return -2; \
    } \
    if (!CHECK_RECV_PKT(sizeof(rsp_type))) { \
        emit messageLogged(DUMP_SEND_PKT); \
        emit messageLogged(DUMP_RECV_PKT); \
        return -3; \
    } \
    rsp_type *rsp = (rsp_type *)recv_pkt->data; \
    if (rsp->ret != 0) { \
        emit messageLogged(DUMP_SEND_PKT); \
        emit messageLogged(DUMP_RECV_PKT); \
        return rsp->ret; \
    }

TaskCommBase::TaskCommBase(EvSerial *serial) : QObject(nullptr)
{
    seq = QRandomGenerator::global()->generate() & 0xFF;
    m_serial = serial;
    setAutoDelete(true);
}

int TaskCommBase::callGetVersion(uint8_t type,
                                uint8_t *major,
                                uint8_t *minor,
                                uint8_t *patch,
                                uint32_t *build_time,
                                uint32_t *firm_len)
{
    PREPARE_PKT(EV_CALL_GET_VERSION, ev_call_get_version_req_t);
    req->type = type;
    SEND_RECV_PKT(500, ev_call_get_version_rsp_t);

    if (major) *major = rsp->major;
    if (minor) *minor = rsp->minor;
    if (patch) *patch = rsp->patch;
    if (build_time) *build_time = rsp->build_time;
    if (firm_len) *firm_len = rsp->firm_len;

    return 0;
}

int TaskCommBase::callReset(uint32_t delay_ms)
{
    PREPARE_PKT(EV_CALL_RESET, ev_call_reset_req_t);
    req->delay_ms = delay_ms;
    SEND_RECV_PKT(500, ev_call_reset_rsp_t);

    return 0;
}


int TaskCommBase::callOtaRead(uint32_t index, uint8_t *data)
{
    PREPARE_PKT(EV_CALL_OTA_READ, ev_call_ota_read_req_t);
    req->idx = index;
    SEND_RECV_PKT(500, ev_call_ota_read_rsp_t);

    if (data) memcpy(data, rsp->data, EV_CALL_OTA_DATA_LEN);

    return 0;
}

int TaskCommBase::callOtaStart(uint32_t firm_len)
{
    PREPARE_PKT(EV_CALL_OTA_START, ev_call_ota_start_req_t);
    req->firm_len = firm_len;
    SEND_RECV_PKT(5000, ev_call_ota_start_rsp_t);

    return 0;
}

int TaskCommBase::callOtaData(uint32_t index, const uint8_t *data)
{
    PREPARE_PKT(EV_CALL_OTA_DATA, ev_call_ota_data_req_t);
    req->idx = index;
    memcpy(req->data, data, EV_CALL_OTA_DATA_LEN);
    SEND_RECV_PKT(500, ev_call_ota_data_rsp_t);

    return 0;
}

int TaskCommBase::callOtaEnd()
{
    PREPARE_PKT1(EV_CALL_OTA_END);
    SEND_RECV_PKT(500, ev_call_ota_end_rsp_t);

    return 0;
}

int TaskCommBase::callSendCommand(int cmd_id, const QByteArray &data)
{
    auto send_pkt = std::make_shared<ev_packet_t>();
    send_pkt->seq = seq ++;
    send_pkt->user = 0;
    send_pkt->cmd = cmd_id;
    send_pkt->len = data.size();
    memcpy(send_pkt->data, data.constData(), data.size());

    bool send_result = EvSerial::safeSendPacket(m_serial, send_pkt);
    std::shared_ptr<ev_packet_t> recv_pkt = nullptr;
    if (send_result) {
        recv_pkt = EvSerial::safeRecvPacket(m_serial, 2000);
    }
    emit messageLogged(DUMP_SEND_PKT);
    if (recv_pkt) { 
        emit messageLogged(DUMP_RECV_PKT);
    }

    return !send_result ? -1 : !recv_pkt ? -2 : 0;
}
